home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-03-20 | 21.2 KB | 747 lines | [TEXT/CWIE] |
- /*
- File: MacCalendar
-
- Contains: Control strip module for displaying a calendar.
-
- Written by: Martin Minow
-
- Copyright: © 1994-1997 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- You may incorporate this sample code into your applications without
- restriction, though the sample code has been provided "AS IS" and the
- responsibility for its operation is 100% yours. However, what you are
- not permitted to do is to redistribute the source as "DSC Sample Code"
- after having made changes. If you're going to re-distribute the source,
- we require that you make it clear in the source that the code was
- descended from Apple Sample Code, but that you've made changes.
-
- Based on the Status Bar Sample.c by Steve Christensen.
-
- File Type sdev
- File Creator SCAL -- registered with DTS
- Resource Type sdev
- Resource ID 0
- Resource Attributes purgeable
-
- Other options (MetroWerks, Think C 7.0):
- Set Require Prototypes, Check Pointer Types, All other warnings.
- Do not set trigraph recognition.
- Enable Apple extensions.
-
- MetroWerks link/project options:
- Link single segment
- Set Project type "Code Segment", Standard Header.
- */
-
- /////////////////////////////////////////////////////////////////////////
-
- // Pick up resource constants common to both control strip
- // module and application.
-
- #include "MacCalendarCommon.h"
-
- /////////////////////////////////////////////////////////////////////////
-
- // Pick up resource constants common to both C and Rez for
- // the control strip module.
-
- #include "MacCalendar.h"
-
- /////////////////////////////////////////////////////////////////////////
-
- // Pick up prototype for the core drawing code.
-
- #include "DrawCalendar.h"
-
- /////////////////////////////////////////////////////////////////////////
-
- // Pick up system types.
-
- #include <Fonts.h>
- #include <Memory.h>
- #include <Menus.h>
- #include <Quickdraw.h>
- #include <Resources.h>
- #include <ToolUtils.h>
- #include <Types.h>
- #include <Windows.h>
- #include <Icons.h>
- #include <ControlStrip.h>
- #include <Gestalt.h>
-
- /////////////////////////////////////////////////////////////////////////
-
- // Metrowerks uses A4 to reference globals. The A4-setup code was copied from the
- // WDEF.c sample included in the Metrowerks DR3 distribution. MPW and Think C
- // use PC-relative addressing in a single-segment code module.
-
- #ifdef __MWERKS__
- #include <A4Stuff.h> // also included in <MacHeaders>
- #include <SetupA4.h> // required to handle callback functions
- #endif
-
- /////////////////////////////////////////////////////////////////////////
-
- // Lots of global constants.
-
- /*
- * Define the patterns as C-strings so they can be addressed as constants
- * within the program.
- */
- #define kWhitePattern ((ConstPatternParam) "\000\000\000\000\000\000\000\000")
- #define kBlackPattern ((ConstPatternParam) "\377\377\377\377\377\377\377\377")
-
- enum {
- kIconWidth = 16 // Width of the icon on the control strip itself.
- };
-
- // Name under which our preferences are saved.
-
- #define kCalendarPrefName "\pMacCalendar Preferences"
-
- // Indices into the STRN_Info STR# resource.
-
- enum {
- kStringHelp = 1,
- kStringFontName,
- kStringFontSize,
- kStringFirstDayOfWeek,
- kStringDayNames
- };
-
- /*
- * This record defines the information we need to draw the calendar. It is initialized
- * when we are called with the sdevInitModule message, and passed to and from the
- * Status Bar manager.
- */
- typedef struct GlobalRecord {
- Handle iconSuite; /* Status bar icon */
- Handle textStrings; /* Balloon help string etc. */
- PicHandle rightArrowPicture; /* Popup arrow */
- SavedSettingsHandle settings; /* Preference settings */
- UInt32 lastSavedModCount; /* The modCount setting when we last saved. */
- } GlobalRecord, *GlobalPtr, **GlobalHandle;
-
- /////////////////////////////////////////////////////////////////////////
-
- // Save current status for restarts.
-
- static OSErr
- CtlStripSavePreferences(
- GlobalPtr globalPtr
- )
- {
- OSErr status;
-
- status = SBSavePreferences(kCalendarPrefName, (Handle) globalPtr->settings);
- if (status == noErr) {
- globalPtr->lastSavedModCount = (**globalPtr->settings).modCount;
- }
-
- return(status);
- }
-
- /////////////////////////////////////////////////////////////////////////
-
- // Termination
-
- static void
- CtlStripCleanUp(
- GlobalHandle globalHandle
- )
- {
- register GlobalPtr globalPtr;
-
- if (globalHandle != nil) {
- HLock((Handle) globalHandle);
- globalPtr = *globalHandle;
- if (globalPtr->iconSuite != NULL)
- DisposeIconSuite(globalPtr->iconSuite, TRUE);
- if (globalPtr->textStrings != NULL)
- DisposeHandle(globalPtr->textStrings);
- if (globalPtr->rightArrowPicture != NULL)
- DisposeHandle((Handle) globalPtr->rightArrowPicture);
- if (globalPtr->settings != NULL)
- DisposeHandle((Handle) globalPtr->settings);
- (void) ReplaceGestaltValue(kControlStripCreator, 0);
- DisposeHandle((Handle) globalHandle);
- }
- }
-
- /////////////////////////////////////////////////////////////////////////
-
- // Initialization
-
- static long
- CtlStripInitialize(void)
- {
- register GlobalHandle globalHandle;
- register GlobalPtr globalPtr;
- long result;
- Str255 work;
- SavedSettingsHandle prefsHandle;
- long tempLong;
- long gestaltResponse;
-
- globalHandle = nil;
-
- // We're using GestaltValue, so we have to make sure we have
- // System 7.5.
-
- result = Gestalt(gestaltSystemVersion, &gestaltResponse);
- if (result == noErr) {
- if (gestaltResponse < 0x0750) {
- result = unimpErr;
- }
- }
-
- // Register ourselves with Gestalt. This will trigger
- // an error if we've been installed twice, which will cause the
- // second instance to fail sooner rather than fail later.
-
- if (result == noErr) {
- result = NewGestaltValue(kControlStripCreator, 0);
- }
- if (result == noErr) {
- globalHandle = (GlobalHandle) NewHandleSysClear(sizeof (GlobalRecord));
- result = noErr;
- if (globalHandle == NULL)
- result = MemError();
- else {
- HLock((Handle) globalHandle);
- globalPtr = *globalHandle;
- /*
- * Load and detach the icon suite
- */
- result = SBGetDetachIconSuite(&(globalPtr->iconSuite), ICON_StatusBar, svAllSmallData);
- }
- }
- if (result == noErr) {
- globalPtr->textStrings = GetResource('STR#', STRN_Info);
- result = ResError();
- }
- if (result == noErr) {
- DetachResource(globalPtr->textStrings);
- globalPtr->rightArrowPicture = GetPicture(PICT_RightArrow);
- if (globalPtr->rightArrowPicture == NULL)
- result = ResError();
- }
- if (result == noErr) {
- DetachResource((Handle) globalPtr->rightArrowPicture);
-
- /*
- * Get the saved preferences, if any, and configure the drawing
- * environment. Note that the sample status bar doesn't dispose
- * of the prefsHandle but I maintain that this is a bug. While
- * the control strip documentation doesn't mention whether the
- * handle returned by SBLoadPreferences is a resource or a memory
- * handle, my testing indicates it's a memory handle.
- */
- prefsHandle = nil;
- result = SBLoadPreferences(kCalendarPrefName, (Handle *) &prefsHandle);
- if (result == noErr
- && prefsHandle != nil
- && GetHandleSize((Handle) prefsHandle) == sizeof (SavedSettings)
- && (**prefsHandle).signature == kControlStripCreator
- && (**prefsHandle).prefVersion == kPrefVersion) {
- /*
- * Use the saved preference resource
- */
- globalPtr->settings = prefsHandle;
- (**(globalPtr->settings)).modCount = 0;
- globalPtr->lastSavedModCount = 0;
- }
- else {
- /*
- * Hmm, we don't have any preferences. Build a new preference resource.
- */
- if (prefsHandle != nil) {
- DisposeHandle( (Handle) prefsHandle);
- }
- prefsHandle = (SavedSettingsHandle) NewHandleSysClear(sizeof(SavedSettings));
- globalPtr->settings = prefsHandle;
- result = MemError();
- if (result == noErr) {
- (**(globalPtr->settings)).signature = kControlStripCreator;
- (**(globalPtr->settings)).prefVersion = kPrefVersion;
- (**(globalPtr->settings)).modCount = 0;
- globalPtr->lastSavedModCount = 1;
-
- SBGetDetachedIndString(work, globalPtr->textStrings, kStringDayNames);
- pstrcpy((**(globalPtr->settings)).dayNameString, work);
-
- SBGetDetachedIndString(work, globalPtr->textStrings, kStringFontName);
- pstrcpy((**(globalPtr->settings)).fontName, work);
-
- SBGetDetachedIndString(work, globalPtr->textStrings, kStringFontSize);
- StringToNum(work, &tempLong);
- (**(globalPtr->settings)).fontSize = tempLong;
-
- SBGetDetachedIndString(work, globalPtr->textStrings, kStringFirstDayOfWeek);
- StringToNum(work, &tempLong);
- (**(globalPtr->settings)).firstDayOfWeek = tempLong;
-
- }
- }
- }
-
- // Now that we successfully started up, publish our globals using
- // Gestalt.
-
- if (result == noErr) {
- result = ReplaceGestaltValue(kControlStripCreator, (long) globalPtr->settings);
- }
- /*
- * We've finished all initialization. If there is an error, exit through
- * CtlStripCleanUp to dispose of handles and other junk. If initialization
- * are successful, unlock the handle and return the handle cast to a long.
- */
- if (result != noErr) {
- CtlStripCleanUp(globalHandle);
- }
- else {
- HUnlock((Handle) globalHandle);
- result = (long) globalHandle;
- }
- return (result);
- }
-
- /////////////////////////////////////////////////////////////////////////
-
- // Draw the icon in the status bar.
-
- static long
- CtlStripDrawStatusIcon(
- GlobalPtr globalPtr,
- const Rect *statusRect
- )
- {
- Rect viewRect;
- short arrowHeight;
-
- viewRect = *statusRect;
- viewRect.right = viewRect.left + kIconWidth;
- (void) PlotIconSuite(&viewRect, atNone, ttNone, globalPtr->iconSuite);
- /*
- * Draw an right-arrow to show that we have a popup menu. Well, we don't
- * actually have a popup menu, but we do pop up a calendar when clicked on.
- */
- arrowHeight = height(PicFrame(rightArrowPicture));
- viewRect.left = viewRect.right;
- viewRect.right += width(PicFrame(rightArrowPicture));
- viewRect.top += ((height(viewRect) - arrowHeight) >> 1);
- viewRect.bottom = viewRect.top + arrowHeight;
- DrawPicture(globalPtr->rightArrowPicture, &viewRect);
- return (0);
- }
-
- /////////////////////////////////////////////////////////////////////////
-
- // Position the calendar with respect to the status bar. If there is enough room
- // above, put it above, else put it below. Left and right operate similarly.
-
- enum {
- kCtlStripFrame = 4 /* The frame above/below the icon itself */
- };
-
- static void
- GetDisplayRect(
- const Rect *statusRect,
- Point displaySize,
- Rect *windowRect
- )
- {
- Rect sBarRect;
-
- sBarRect = *statusRect;
- LocalToGlobal((Point *) &sBarRect.top);
- LocalToGlobal((Point *) &sBarRect.bottom);
- if (sBarRect.top - kCtlStripFrame - displaySize.v - (GetMBarHeight() + 2) > 0) {
- /*
- * The calendar is displayed above the status bar.
- */
- windowRect->bottom = sBarRect.top - kCtlStripFrame - 1;
- windowRect->top = windowRect->bottom - displaySize.v;
- }
- else {
- /*
- * The calendar is displayed below the status bar.
- */
- windowRect->top = sBarRect.bottom + kCtlStripFrame + 1;
- windowRect->bottom = windowRect->top + displaySize.v;
- }
- if (sBarRect.right - displaySize.h > 0) {
- /*
- * The calendar is displayed to the left of the calendar icon.
- */
- windowRect->right = sBarRect.right;
- windowRect->left = windowRect->right - displaySize.h;
- }
- else {
- /*
- * The calendar is displayed to the right of the calendar icon.
- */
- windowRect->left = sBarRect.left;
- windowRect->right = windowRect->left + displaySize.h;
- }
- }
-
-
- /////////////////////////////////////////////////////////////////////////
-
- // Build the triangular "next month" and "previous month" buttons.
-
- enum {
- kButtonSeparation = 4 /* 1.0d3, was 2 */
- };
-
- static void
- MakeTriangularButtons(
- const Rect *monthRect,
- PolyHandle *leftButton,
- PolyHandle *rightButton,
- Rect *leftButtonRect,
- Rect *rightButtonRect
- )
- {
- FontInfo fontInfo;
- short buttonSize;
- short halfSize;
- Rect bothButtonRect;
-
- GetFontInfo(&fontInfo);
- buttonSize = (fontInfo.ascent & ~1); /* Round down to even value */
- halfSize = buttonSize / 2;
- bothButtonRect = *monthRect;
- bothButtonRect.bottom -= (1 + fontInfo.leading);
- bothButtonRect.top = bothButtonRect.bottom - buttonSize;
- bothButtonRect.left =
- (width(*monthRect) >> 1) - buttonSize - kButtonSeparation;
- bothButtonRect.right =
- (width(*monthRect) >> 1) + buttonSize + kButtonSeparation;
- *leftButtonRect = bothButtonRect;
- /* 1.0d4 + */
- leftButtonRect->right = leftButtonRect->left + halfSize;
- *rightButtonRect = bothButtonRect;
- rightButtonRect->left = rightButtonRect->right - halfSize;
- *leftButton = OpenPoly();
- MoveTo(halfSize, 0);
- LineTo(halfSize, buttonSize);
- LineTo(0, halfSize);
- LineTo(halfSize, 0);
- ClosePoly();
- OffsetPoly(*leftButton, leftButtonRect->left, leftButtonRect->top);
- *rightButton = OpenPoly();
- MoveTo(0, 0);
- LineTo(halfSize, halfSize);
- LineTo(0, buttonSize);
- LineTo(0, 0);
- ClosePoly();
- OffsetPoly(*rightButton, rightButtonRect->left, rightButtonRect->top);
- /* 1.0d4 - */
- }
-
- /////////////////////////////////////////////////////////////////////////
-
- // Now that we've done setup, do the actual mouse tracking. If the mouse hits the
- // right button, advance the month and draw it. If it hits the left button, draw
- // the previous month.
-
- static void
- DrawCalendarAndTrackMouse(
- GlobalPtr globalPtr,
- WindowPtr windowPtr,
- const Rect *monthRect
- )
- {
- Rect leftButtonRect;
- Rect rightButtonRect;
- PolyHandle leftButton;
- PolyHandle rightButton;
- unsigned long nowSeconds;
- DateTimeRec now;
- Point mousePt;
- short thisYear;
- short thisMonth;
- typedef enum {
- inNoButton = 0,
- inLeftButton = 1,
- inRightButton = 2
- } WhichButton;
- WhichButton inButton;
- WhichButton wasInButton;
- unsigned long nextMonthTick;
- Boolean redrawButtons;
-
- FrameRect(monthRect);
- MakeTriangularButtons(
- monthRect,
- &leftButton,
- &rightButton,
- &leftButtonRect,
- &rightButtonRect
- );
- GetDateTime(&nowSeconds);
- SecondsToDate(nowSeconds, &now);
- inButton = wasInButton = inNoButton;
- thisYear = thisMonth = 0;
- InitCursor();
- while (WaitMouseUp()) {
- if (thisYear != now.year || thisMonth != now.month) {
- /*
- * Draw the new month. Also make sure the buttons are drawn.
- */
- redrawButtons = TRUE;
- EraseRect(&windowPtr->portRect);
- FrameRect(monthRect);
- switch (wasInButton) {
- case inLeftButton:
- FillPoly(leftButton, kWhitePattern);
- break;
- case inRightButton:
- FillPoly(rightButton, kWhitePattern);
- break;
- }
- FramePoly(leftButton);
- FramePoly(rightButton);
- DrawCalendar(
- globalPtr->settings,
- now.year,
- now.month,
- &windowPtr->portRect
- );
- thisYear = now.year;
- thisMonth = now.month;
- } /* If drawing new month */
- /*
- * Get the mouse and track it while it is in one of our buttons
- */
- GetMouse(&mousePt);
- if (PtInRect(mousePt, &leftButtonRect))
- inButton = inLeftButton;
- else if (PtInRect(mousePt, &rightButtonRect))
- inButton = inRightButton;
- else {
- inButton = inNoButton;
- }
- if (redrawButtons || inButton != wasInButton) {
- switch (wasInButton) {
- case inLeftButton: FillPoly(leftButton, kWhitePattern); break;
- case inRightButton: FillPoly(rightButton, kWhitePattern); break;
- }
- switch (inButton) {
- case inLeftButton: FillPoly(leftButton, kBlackPattern); break;
- case inRightButton: FillPoly(rightButton, kBlackPattern); break;
- }
- FramePoly(leftButton);
- FramePoly(rightButton);
- if (inButton != wasInButton && inButton != inNoButton)
- nextMonthTick = 0; /* Force new month drawing */
- redrawButtons = FALSE;
- wasInButton = inButton;
- } /* If button click change */
- if (inButton != inNoButton && TickCount() > nextMonthTick) {
- /*
- * The user has clicked in a button, or has held the mouse
- * down in a button for one second. Draw the appropriate month.
- */
- nextMonthTick = TickCount() + 60;
- switch (inButton) {
- case inLeftButton:
- if (--now.month <= 0) { /* Previous month or year */
- now.month = 12;
- --now.year;
- }
- break;
- case inRightButton:
- if (++now.month > 12) { /* Next month or year */
- now.month = 1;
- ++now.year;
- }
- break;
- } /* Which button was clicked */
- } /* Moving to a new month */
- } /* Loop while mouse down */
- KillPoly(leftButton);
- KillPoly(rightButton);
- }
-
- /////////////////////////////////////////////////////////////////////////
-
- // Click in the status bar icon
-
- static long
- CtlStripMouseClick(
- GlobalPtr globalPtr,
- const Rect *statusRect
- )
- {
- Rect windowRect;
- WindowPtr windowPtr;
- GrafPtr savePort;
- Point displaySize;
- Rect monthRect;
-
- displaySize = GetCalendarDisplaySize(globalPtr->settings);
- displaySize.h += 2;
- displaySize.v += 2;
- GetDisplayRect(statusRect, displaySize, &windowRect);
- windowPtr = NewWindow(
- NULL,
- &windowRect,
- "\p",
- TRUE,
- plainDBox,
- (WindowPtr) -1L,
- FALSE, /* No go-away box */
- 0 /* No refCon */
- );
- if (windowPtr != NULL) {
- GetPort(&savePort);
- SetPort(windowPtr);
- GetCalendarMonthRect(
- globalPtr->settings,
- &windowPtr->portRect,
- &monthRect
- );
- if (StillDown()) {
- /*
- * Design the two triangular buttons that will be displayed on the
- * bottom line of the calendar and create the polygons. Then draw
- * the calendar and track the mouse while it's held down.
- */
- DrawCalendarAndTrackMouse(
- globalPtr,
- windowPtr,
- &monthRect
- );
- }
- SetPort(savePort);
- DisposeWindow(windowPtr);
- }
- return (0);
- }
-
- /////////////////////////////////////////////////////////////////////////
-
- // Periodically we check the globals that we publish via Gestalt. If they
- // have been modified, we return sdevNeedToSave so that the Control Strip
- // will call our CtlStripSavePreferences routine when an appropriate time
- // to save comes around (ie the hard disk spins up).
-
- static long
- CtlStripPeriodicTickle(
- GlobalPtr globalPtr,
- const Rect *statusRect
- )
- {
- #pragma unused(statusRect)
- OSErr result;
-
- result = 0;
- if (globalPtr->lastSavedModCount != (**globalPtr->settings).modCount) {
- result = (1 << sdevNeedToSave);
- }
- return (result);
- }
-
- /////////////////////////////////////////////////////////////////////////
-
- // This "main" program is called by the Control Strip manager. Note that,
- // unlike the rest of this program, we must define a prototype for main
- // because we can't just declare it "static".
-
- pascal long main(
- unsigned long message,
- GlobalHandle globalHandle,
- const Rect *statusRect,
- GrafPtr statusPort
- );
-
- pascal long
- main(
- unsigned long message,
- GlobalHandle globalHandle,
- const Rect *statusRect,
- GrafPtr statusPort
- )
- {
- #pragma unused(statusPort)
- short savedState;
- register GlobalPtr globalPtr;
- long result;
- Str255 helpString;
- #ifdef __MWERKS__
- long oldA4 = SetCurrentA4();
- #endif
-
- if (globalHandle != nil) {
- /*
- * We have already allocated the global record. Save its lock state, lock
- * the handle, and get the global pointer (so we can write (*globalPtr).something)
- */
- savedState = HGetState((Handle) globalHandle);
- HLock((Handle) globalHandle);
- globalPtr = *globalHandle;
- }
- result = 0; /* Unknown message result */
- switch (message) {
- case sdevInitModule: /* Initialize the module */
- /*
- * Initialization always sets globalHandle to NULL to avoid the HSetState
- * at the exit routine. If CtlStripInitialize succeeds, it sets the result
- * to the global parameter.
- */
- globalHandle = NULL;
- result = CtlStripInitialize(); /* Do the initialization and */
- break; /* Return global or error code */
- case sdevCloseModule: /* Clean up before closing */
- CtlStripCleanUp(globalHandle);
- globalHandle = NULL;
- break;
- case sdevFeatures: /* Return feature bits */
- result = ( (1<<sdevWantMouseClicks) /* We handle mouse down */
- | (1<<sdevDontAutoTrack) /* We track the mouse, too */
- | (1<<sdevHasCustomHelp) /* Custom help string */
- );
- break;
- case sdevGetDisplayWidth: /* Return display width */
- result = kIconWidth + width(PicFrame(rightArrowPicture));
- break;
- case sdevPeriodicTickle: /* Nothing else is happening */
- result = CtlStripPeriodicTickle(globalPtr, statusRect);
- break;
- case sdevDrawStatus: /* Draw the status bar info */
- result = CtlStripDrawStatusIcon(globalPtr, statusRect);
- break;
- case sdevMouseClick: /* Status bar click */
- result = CtlStripMouseClick(globalPtr, statusRect);
- break;
- case sdevSaveSettings: /* Save changed settings */
- result = CtlStripSavePreferences(globalPtr);
- break;
- case sdevShowBalloonHelp: /* Display custom balloon help */
- /*
- * We don't really have a custom help string, but this shows how to do it.
- */
- SBGetDetachedIndString(helpString, globalPtr->textStrings, kStringHelp);
- SBShowHelpString(statusRect, helpString);
- break;
- default:
- // Ignore unknown messages.
- break;
- }
-
- if (globalHandle != NULL) /* If we have globals allocated */
- HSetState((Handle) globalHandle, savedState); /* Restore lock state */
-
- #ifdef __MWERKS__
- SetA4(oldA4);
- #endif
- return (result);
- }
-
-
-